module objects.bezierpatch;

import utils.vector3;

private float B3(float u, int n) {
    switch (n) {
        case 0: return u*u*u;
        case 1: return 3*u*u*(1-u);
        case 2: return 3*u*(1-u)*(1-u);
        case 3: return (1-u)*(1-u)*(1-u);
    }
}

private float B2(float u, int n) {
    switch (n) {
        case 0: return u*u;
        case 1: return 2*u*(1-u);
        case 2: return (1-u)*(1-u);
    }
}

public Vector3f bernstein(float u, Vector3f[4] p) {
    return B3(u,0)*p[0]+B3(u,1)*p[1]+B3(u,2)*p[2]+B3(u,3)*p[3];
}

public Vector3f bernstein2(float u, Vector3f[4] p) {
    return B2(u,0)*(p[1] - p[0]) + B2(u,1)*(p[2] - p[1]) +
        B2(u,2)*(p[3] - p[2]);
}

struct Bezier4x4Patch {
    Vector3f[4][4] vertices;

    Vector3f normal(float u, float v) {
        Vector3f[4] tmp;
        tmp[0] = bernstein(u, vertices[0]);
        tmp[1] = bernstein(u, vertices[1]);
        tmp[2] = bernstein(u, vertices[2]);
        tmp[3] = bernstein(u, vertices[3]);
        Vector3f n1 = bernstein2(v,tmp);

        tmp[0] = bernstein(v, [vertices[0][0],vertices[1][0],vertices[2][0],vertices[3][0]]);
        tmp[1] = bernstein(v, [vertices[0][1],vertices[1][1],vertices[2][1],vertices[3][1]]);
        tmp[2] = bernstein(v, [vertices[0][2],vertices[1][2],vertices[2][2],vertices[3][2]]);
        tmp[3] = bernstein(v, [vertices[0][3],vertices[1][3],vertices[2][3],vertices[3][3]]);
        Vector3f n2 = bernstein2(u,tmp);

        return (n2 | n1).normal();
    }

}
